package raycast;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import model.Scene;
import model.primitive.Primitive;
import model.ray.Ray;
import camera.PinHoleCamera;

public class RayCaster implements Renderer {
	
	private final Color DEF_COLOR = Color.WHITE;	//Color del fondo si no hubo interseccion
	
	public BufferedImage Render(Scene scene, int width, int height) {
		BufferedImage image = new BufferedImage( width, height, BufferedImage.TYPE_INT_RGB );
		PinHoleCamera camera = ((PinHoleCamera)scene.getCamera());
		Vector3d cameye;
		
		cameye = camera.eye;
		
		Point3d origin = new Point3d(cameye.x, cameye.y, cameye.z);
	  	Vector3d widthReference = new Vector3d();	  	
	  	Vector3d heightReference = new Vector3d();
		Vector3d direction = new Vector3d();
		
		//Seteamos un rayo que empieza en el origen (por ahora no tiene direccion)
		Ray ray = new Ray(origin, null);
		
		//Para determinar las direcciones de los rayos se
		//van haciendo las interpolaciones de los pares de vectores (tl, bl) y (tr, br) en H pasos
		//Se debe interpolar cada resultado del primer par, con los del segundo par 
		//en W pasos
		
		//Creo el vector "widthReference" que va desde TL hasta TR y
		//el vector "heightReference" que va desde TL a BL 
		//
	  	widthReference.sub(camera.TR_vector(), camera.TL_vector());
	  	//Seteo este valor escalar, asi tengo una referencia del incremento para que
	  	//luego de "W" pasos cubrir una distancia igual a "width" 
	  	widthReference.scale(1.0/width);	  
	  	heightReference.sub(camera.BL_vector(), camera.TL_vector());
	  	//Idem
	  	heightReference.scale(1.0/height);
		
	  	//Arranco setendo actualheight como el vector TL
	  	Vector3d actualHeight = new Vector3d(camera.TL_vector());
		Vector3d actualWidth = new Vector3d();
		
		for ( int y = 0; y < height; y++ ){
			//En cada vuelta actualizo el vector actualWidth  
			actualWidth.set(actualHeight);
			for ( int x = 0; x < width; x++ ){
				direction.set(actualWidth);
				direction.normalize();		
				//Recordar que "ray" siempre tiene origen en "eye". Asi le voy seteando las direcciones correspondientes 
				ray.setDirection(direction);
 
				Primitive primitive = scene.findIntersection(ray);	//Metodo CLAVE!!! (Aca podemos cambiar si intersectamos a cierta distancia para cambiar de color)
				if( primitive != null )
					image.setRGB(x, y, primitive.getShader().getColor(null, null, null).getRGB());
				else
					image.setRGB(x, y, DEF_COLOR.getRGB() );
				actualWidth.add(widthReference);
			}
			actualHeight.add(heightReference);
		}
		return image;
	}
	
	//Test method for BufferedImage class
	public BufferedImage makeGenericImage( int sizeX, int sizeY ){
        BufferedImage image = new BufferedImage( sizeX, sizeY, BufferedImage.TYPE_INT_RGB );
        for (int x = 0; x < sizeX; x++){
            for (int y = 0; y < sizeY; y++){
            	if( y % 2 == 0 && x % 2 == 0 )
            		image.setRGB(x, y, Color.WHITE.getRGB() );
            	else if( y % 2 == 0 && x % 2 == 1 )
            		image.setRGB(x, y, Color.BLUE.getRGB() );
            	else if( y % 2 == 1 && x % 2 == 0 )
            		image.setRGB(x, y, Color.RED.getRGB() );
            	else 
            		image.setRGB(x, y, Color.YELLOW.getRGB() );
            }
        }
        return image;
    }

	public void savePNG( final BufferedImage bi, final String path ){
        try {
            RenderedImage rendImage = bi;
            ImageIO.write(rendImage, "PNG", new File(path));
        } catch ( IOException e) {
            e.printStackTrace();
        }
	}
	
	public void saveBMP( final BufferedImage bi, final String path ){
        try {
            RenderedImage rendImage = bi;
            ImageIO.write(rendImage, "BMP", new File(path));
        } catch ( IOException e) {
            e.printStackTrace();
        }
	}
}
